
package com.dubbo.util;

import com.alibaba.dubbo.common.Constants;
import com.alibaba.dubbo.common.URL;
import com.alibaba.dubbo.common.utils.NetUtils;
import com.alibaba.dubbo.registry.RegistryService;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Logger;

/**
 * @author yax
 * 连接dubbo服务的zookeeper注册中心
 * 将dubbo服务的信息动态的封装到本地变量中
 * 为dubbo监控服务的核心类
 */
//@Service
//@Lazy
public class RegistryContainer {

    private static final Logger LOG = Logger.getLogger("RegistryContainer");


    private RegistryService registry;

    private DubboReferenceUtil dubboReferenceUtil;
    private Map<String, List<String>>  serviceMethod=new ConcurrentHashMap<>();

    private Map<String,Set<String>> serviceAddress=new ConcurrentHashMap<>();

    public Map<String, Set<String>> getServiceAddress() {
        return serviceAddress;
    }

    public Map<String, List<String>> getServiceMethod(){
        return serviceMethod;
    }
    public Set<String> getRpcInterfaces(){
        return serviceMethod.keySet();
    }
    public List<String> getRpcMethods(String interfaceName){
        return serviceMethod.get(interfaceName);
    }
    public Set<String> getRpcAddress(String interfaceName){
        return serviceAddress.get(interfaceName);
    }
    public RegistryService getRegistry() {
        return registry;
    }

    /**
     * 容器初始化的时候执行的方法
     * 会在servlet容器初始化的时候请求注册中心将注册中心中的数据动态的封装到本地的变量中
     */
    @PostConstruct
    public void start() {
         dubboReferenceUtil=SpringContextUtil.getBean(DubboReferenceUtil.class);
        registry=dubboReferenceUtil.getDubboBeanByClass(RegistryService.class);
        //创建指向注册中心zookeeper的url对象 该对象声明返回需要的数据
        new Thread(()->{
            URL subscribeUrl = new URL(Constants.ADMIN_PROTOCOL, NetUtils.getLocalHost(), 0, "",
                    Constants.INTERFACE_KEY, Constants.ANY_VALUE,
                    Constants.GROUP_KEY, Constants.ANY_VALUE,
                    Constants.VERSION_KEY, Constants.ANY_VALUE,
                    Constants.CLASSIFIER_KEY, Constants.ANY_VALUE,
                    Constants.CATEGORY_KEY, Constants.PROVIDERS_CATEGORY + ","
                    + Constants.CONSUMERS_CATEGORY,
                    Constants.CHECK_KEY, String.valueOf(false));
            //执行认证 并设置监听类动态的接收从注册中心返回的数据
            //返回的数据封装在urls中
            registry.subscribe(subscribeUrl, urls -> {
                //判断urls是否为空 如果为空 直接结束方法 不做任何处理
                if (urls == null || urls.size() == 0) {
                    return;
                }
                for(URL url:urls){
                    //获得服务的接口名称并封装到集合中 该名称为接口的全类名
                    String service = url.getServiceInterface();
                    //获得url服务的种类 默认为服务的提供者 默认值是通过方法的第二个参数来进行设定的
                    String category = url.getParameter(Constants.CATEGORY_KEY, Constants.DEFAULT_CATEGORY);
                    //如果返回的url为服务的提供者
                    if (Constants.PROVIDERS_CATEGORY.equals(category)) {
                        if (Constants.EMPTY_PROTOCOL.equals(url.getProtocol())) {
                            serviceMethod.remove(service);
                        }else{
                            String address="dubbo://"+url.getAddress();
                            //获取该接口的方法集合(用,分割)
                            String methods=url.getParameter(Constants.METHODS_KEY);
                            if(!serviceMethod.containsKey(service)){
                                String[] methodArray=methods.split(",");
                                serviceMethod.put(service,new ArrayList<>(Arrays.asList(methodArray)));
                                serviceAddress.put(service,new HashSet<>(Arrays.asList(new String[]{address})));
                            }else{
                                String[] methodArray=methods.split(",");
                                List<String> method=serviceMethod.get(service);
                                if(methodArray.length>method.size()){
                                    serviceMethod.put(service,new ArrayList<>(Arrays.asList(methodArray)));
                                }
                                Set<String> addressList=serviceAddress.get(service);
                                addressList.add(address);
                            }
                        }
                    }
                }
            });
       }).start();
    }

    @PreDestroy
    public void stop(){
    }

}
